//! Tests that don't fit into a specific category.

use expect_test::{Expect, expect};
use ide_db::SymbolKind;

use crate::{
    CompletionItemKind,
    tests::{
        TEST_CONFIG, check, check_edit, check_no_kw, check_with_trigger_character,
        do_completion_with_config,
    },
};

#[test]
fn completes_if_prefix_is_keyword() {
    check_edit(
        "wherewolf",
        r#"
fn main() {
    let wherewolf = 92;
    drop(where$0)
}
"#,
        r#"
fn main() {
    let wherewolf = 92;
    drop(wherewolf)
}
"#,
    )
}

/// Regression test for issue #6091.
#[test]
fn correctly_completes_module_items_prefixed_with_underscore() {
    check_edit(
        "_alpha",
        r#"
fn main() {
    _$0
}
fn _alpha() {}
"#,
        r#"
fn main() {
    _alpha();$0
}
fn _alpha() {}
"#,
    )
}

#[test]
fn completes_prelude() {
    check_no_kw(
        r#"
//- /main.rs edition:2018 crate:main deps:std
fn foo() { let x: $0 }

//- /std/lib.rs crate:std
pub mod prelude {
    pub mod rust_2018 {
        pub struct Option;
    }
}
"#,
        expect![[r#"
            md std
            st Option Option
            bt u32       u32
        "#]],
    );
}

#[test]
fn completes_prelude_macros() {
    check_no_kw(
        r#"
//- /main.rs edition:2018 crate:main deps:std
fn f() {$0}

//- /std/lib.rs crate:std
pub mod prelude {
    pub mod rust_2018 {
        pub use crate::concat;
    }
}

mod macros {
    #[rustc_builtin_macro]
    #[macro_export]
    macro_rules! concat { }
}
"#,
        expect![[r#"
            fn f()                       fn()
            ma concat!(…) macro_rules! concat
            md std
            bt u32                        u32
        "#]],
    );
}

#[test]
fn completes_std_prelude_if_core_is_defined() {
    check_no_kw(
        r#"
//- /main.rs crate:main deps:core,std edition:2021
fn foo() { let x: $0 }

//- /core/lib.rs crate:core
pub mod prelude {
    pub mod rust_2021 {
        pub struct Option;
    }
}

//- /std/lib.rs crate:std deps:core
pub mod prelude {
    pub mod rust_2021 {
        pub struct String;
    }
}
"#,
        expect![[r#"
            md core
            md std
            st String String
            bt u32       u32
        "#]],
    );
}

#[test]
fn respects_doc_hidden() {
    check_no_kw(
        r#"
//- /lib.rs crate:lib deps:std
fn f() {
    format_$0
}

//- /std.rs crate:std
#[doc(hidden)]
#[macro_export]
macro_rules! format_args_nl {
    () => {}
}

pub mod prelude {
    pub mod rust_2018 {}
}
            "#,
        expect![[r#"
            fn f() fn()
            md std
            bt u32  u32
        "#]],
    );
}

#[test]
fn respects_doc_hidden_in_assoc_item_list() {
    check_no_kw(
        r#"
//- /lib.rs crate:lib deps:std
struct S;
impl S {
    format_$0
}

//- /std.rs crate:std
#[doc(hidden)]
#[macro_export]
macro_rules! format_args_nl {
    () => {}
}

pub mod prelude {
    pub mod rust_2018 {}
}
            "#,
        expect![[r#"
                md std
            "#]],
    );
}

#[test]
fn associated_item_visibility() {
    check_no_kw(
        r#"
//- /lib.rs crate:lib new_source_root:library
pub struct S;

impl S {
    pub fn public_method() { }
    fn private_method() { }
    pub type PublicType = u32;
    type PrivateType = u32;
    pub const PUBLIC_CONST: u32 = 1;
    const PRIVATE_CONST: u32 = 1;
}

//- /main.rs crate:main deps:lib new_source_root:local
fn foo() { let _ = lib::S::$0 }
"#,
        expect![[r#"
            ct PUBLIC_CONST pub const PUBLIC_CONST: u32
            fn public_method()                     fn()
            ta PublicType     pub type PublicType = u32
        "#]],
    );
}

#[test]
fn completes_union_associated_method() {
    check_no_kw(
        r#"
union U {};
impl U { fn m() { } }

fn foo() { let _ = U::$0 }
"#,
        expect![[r#"
            fn m() fn()
        "#]],
    );
}

#[test]
fn completes_trait_associated_method_1() {
    check_no_kw(
        r#"
trait Trait { fn m(); }

fn foo() { let _ = Trait::$0 }
"#,
        expect![[r#"
            fn m() (as Trait) fn()
        "#]],
    );
}

#[test]
fn completes_trait_associated_method_2() {
    check_no_kw(
        r#"
trait Trait { fn m(); }

struct S;
impl Trait for S {}

fn foo() { let _ = S::$0 }
"#,
        expect![[r#"
            fn m() (as Trait) fn()
        "#]],
    );
}

#[test]
fn completes_trait_associated_method_3() {
    check_no_kw(
        r#"
trait Trait { fn m(); }

struct S;
impl Trait for S {}

fn foo() { let _ = <S as Trait>::$0 }
"#,
        expect![[r#"
            fn m() (as Trait) fn()
        "#]],
    );
}

#[test]
fn completes_ty_param_assoc_ty() {
    check_no_kw(
        r#"
trait Super {
    type Ty;
    const CONST: u8;
    fn func() {}
    fn method(&self) {}
}

trait Sub: Super {
    type SubTy;
    const C2: ();
    fn subfunc() {}
    fn submethod(&self) {}
}

fn foo<T: Sub>() { T::$0 }
"#,
        expect![[r#"
            ct C2 (as Sub)         const C2: ()
            ct CONST (as Super) const CONST: u8
            fn func() (as Super)           fn()
            fn subfunc() (as Sub)          fn()
            me method(…) (as Super)   fn(&self)
            me submethod(…) (as Sub)  fn(&self)
            ta SubTy (as Sub)        type SubTy
            ta Ty (as Super)            type Ty
        "#]],
    );
}

#[test]
fn completes_self_param_assoc_ty() {
    check_no_kw(
        r#"
trait Super {
    type Ty;
    const CONST: u8 = 0;
    fn func() {}
    fn method(&self) {}
}

trait Sub: Super {
    type SubTy;
    const C2: () = ();
    fn subfunc() {}
    fn submethod(&self) {}
}

struct Wrap<T>(T);
impl<T> Super for Wrap<T> {}
impl<T> Sub for Wrap<T> {
    fn subfunc() {
        // Should be able to assume `Self: Sub + Super`
        Self::$0
    }
}
"#,
        expect![[r#"
            ct C2 (as Sub)         const C2: ()
            ct CONST (as Super) const CONST: u8
            fn func() (as Super)           fn()
            fn subfunc() (as Sub)          fn()
            me method(…) (as Super)   fn(&self)
            me submethod(…) (as Sub)  fn(&self)
            ta SubTy (as Sub)        type SubTy
            ta Ty (as Super)            type Ty
        "#]],
    );
}

#[test]
fn completes_type_alias() {
    check_no_kw(
        r#"
struct S;
impl S { fn foo() {} }
type T = S;
impl T { fn bar() {} }

fn main() { T::$0; }
"#,
        expect![[r#"
            fn bar() fn()
            fn foo() fn()
        "#]],
    );
}

#[test]
fn completes_qualified_macros() {
    check_no_kw(
        r#"
#[macro_export]
macro_rules! foo { () => {} }

fn main() { let _ = crate::$0 }
"#,
        expect![[r#"
            fn main()              fn()
            ma foo!(…) macro_rules! foo
        "#]],
    );
}

#[test]
fn does_not_complete_non_fn_macros() {
    check_no_kw(
        r#"
mod m {
    #[rustc_builtin_macro]
    pub macro Clone {}
}

fn f() {m::$0}
"#,
        expect![[r#""#]],
    );
    check_no_kw(
        r#"
mod m {
    #[rustc_builtin_macro]
    pub macro bench {}
}

fn f() {m::$0}
"#,
        expect![[r#""#]],
    );
}

#[test]
fn completes_reexported_items_under_correct_name() {
    check_no_kw(
        r#"
fn foo() { self::m::$0 }

mod m {
    pub use super::p::wrong_fn as right_fn;
    pub use super::p::WRONG_CONST as RIGHT_CONST;
    pub use super::p::WrongType as RightType;
}
mod p {
    pub fn wrong_fn() {}
    pub const WRONG_CONST: u32 = 1;
    pub struct WrongType {};
}
"#,
        expect![[r#"
            ct RIGHT_CONST     u32
            fn right_fn()     fn()
            st RightType WrongType
        "#]],
    );

    check_edit(
        "RightType",
        r#"
fn foo() { self::m::$0 }

mod m {
    pub use super::p::wrong_fn as right_fn;
    pub use super::p::WRONG_CONST as RIGHT_CONST;
    pub use super::p::WrongType as RightType;
}
mod p {
    pub fn wrong_fn() {}
    pub const WRONG_CONST: u32 = 1;
    pub struct WrongType {};
}
"#,
        r#"
fn foo() { self::m::RightType }

mod m {
    pub use super::p::wrong_fn as right_fn;
    pub use super::p::WRONG_CONST as RIGHT_CONST;
    pub use super::p::WrongType as RightType;
}
mod p {
    pub fn wrong_fn() {}
    pub const WRONG_CONST: u32 = 1;
    pub struct WrongType {};
}
"#,
    );
}

#[test]
fn completes_in_simple_macro_call() {
    check_no_kw(
        r#"
macro_rules! m { ($e:expr) => { $e } }
fn main() { m!(self::f$0); }
fn foo() {}
"#,
        expect![[r#"
            fn foo()  fn()
            fn main() fn()
        "#]],
    );
}

#[test]
fn function_mod_share_name() {
    check_no_kw(
        r#"
fn foo() { self::m::$0 }

mod m {
    pub mod z {}
    pub fn z() {}
}
"#,
        expect![[r#"
            fn z() fn()
            md z
        "#]],
    );
}

#[test]
fn completes_hashmap_new() {
    check_no_kw(
        r#"
struct RandomState;
struct HashMap<K, V, S = RandomState> {}

impl<K, V> HashMap<K, V, RandomState> {
    pub fn new() -> HashMap<K, V, RandomState> { }
}
fn foo() {
    HashMap::$0
}
"#,
        expect![[r#"
            fn new() fn() -> HashMap<K, V, RandomState>
        "#]],
    );
}

#[test]
fn completes_variant_through_self() {
    cov_mark::check!(completes_variant_through_self);
    check_no_kw(
        r#"
enum Foo {
    Bar,
    Baz,
}

impl Foo {
    fn foo(self) {
        Self::$0
    }
}
"#,
        expect![[r#"
            me foo(…) fn(self)
            ev Bar         Bar
            ev Baz         Baz
        "#]],
    );
}

#[test]
fn completes_non_exhaustive_variant_within_the_defining_crate() {
    check_no_kw(
        r#"
enum Foo {
    #[non_exhaustive]
    Bar,
    Baz,
}

fn foo(self) {
    Foo::$0
}
"#,
        expect![[r#"
            ev Bar Bar
            ev Baz Baz
        "#]],
    );

    check_no_kw(
        r#"
//- /main.rs crate:main deps:e
fn foo(self) {
    e::Foo::$0
}

//- /e.rs crate:e
enum Foo {
    #[non_exhaustive]
    Bar,
    Baz,
}
"#,
        expect![[r#"
            ev Baz Baz
        "#]],
    );
}

#[test]
fn completes_primitive_assoc_const() {
    cov_mark::check!(completes_primitive_assoc_const);
    check_no_kw(
        r#"
//- /lib.rs crate:lib deps:core
fn f() {
    u8::$0
}

//- /core.rs crate:core
#![rustc_coherence_is_core]
#[lang = "u8"]
impl u8 {
    pub const MAX: Self = 255;

    pub fn func(self) {}
}
"#,
        expect![[r#"
            ct MAX pub const MAX: Self
            me func(…)        fn(self)
        "#]],
    );
}

#[test]
fn completes_variant_through_alias() {
    cov_mark::check!(completes_variant_through_alias);
    check_no_kw(
        r#"
enum Foo {
    Bar
}
type Foo2 = Foo;
fn main() {
    Foo2::$0
}
"#,
        expect![[r#"
            ev Bar Bar
        "#]],
    );
}

#[test]
fn respects_doc_hidden2() {
    check_no_kw(
        r#"
//- /lib.rs crate:lib deps:dep
fn f() {
    dep::$0
}

//- /dep.rs crate:dep
#[doc(hidden)]
#[macro_export]
macro_rules! m {
    () => {}
}

#[doc(hidden)]
pub fn f() {}

#[doc(hidden)]
pub struct S;

#[doc(hidden)]
pub mod m {}
            "#,
        expect![[r#""#]],
    )
}

#[test]
fn type_anchor_empty() {
    check_no_kw(
        r#"
trait Foo {
    fn foo() -> Self;
}
struct Bar;
impl Foo for Bar {
    fn foo() -> {
        Bar
    }
}
fn bar() -> Bar {
    <_>::$0
}
"#,
        expect![[r#"
            fn foo() (as Foo) fn() -> Self
            ex Bar
            ex Bar::foo()
            ex bar()
        "#]],
    );
}

#[test]
fn type_anchor_type() {
    check_no_kw(
        r#"
trait Foo {
    fn foo() -> Self;
}
struct Bar;
impl Bar {
    fn bar() {}
}
impl Foo for Bar {
    fn foo() -> {
        Bar
    }
}
fn bar() -> Bar {
    <Bar>::$0
}
"#,
        expect![[r#"
            fn bar()                  fn()
            fn foo() (as Foo) fn() -> Self
            ex Bar
            ex Bar::foo()
            ex bar()
        "#]],
    );
}

#[test]
fn type_anchor_type_trait() {
    check_no_kw(
        r#"
trait Foo {
    fn foo() -> Self;
}
struct Bar;
impl Bar {
    fn bar() {}
}
impl Foo for Bar {
    fn foo() -> {
        Bar
    }
}
fn bar() -> Bar {
    <Bar as Foo>::$0
}
"#,
        expect![[r#"
            fn foo() (as Foo) fn() -> Self
            ex Bar
            ex Bar::foo()
            ex bar()
        "#]],
    );
}

#[test]
fn completes_fn_in_pub_trait_generated_by_macro() {
    check_no_kw(
        r#"
mod other_mod {
    macro_rules! make_method {
        ($name:ident) => {
            fn $name(&self) {}
        };
    }

    pub trait MyTrait {
        make_method! { by_macro }
        fn not_by_macro(&self) {}
    }

    pub struct Foo {}

    impl MyTrait for Foo {}
}

fn main() {
    use other_mod::{Foo, MyTrait};
    let f = Foo {};
    f.$0
}
"#,
        expect![[r#"
            me by_macro() (as MyTrait)     fn(&self)
            me not_by_macro() (as MyTrait) fn(&self)
        "#]],
    )
}

#[test]
fn completes_fn_in_pub_trait_generated_by_recursive_macro() {
    check_no_kw(
        r#"
mod other_mod {
    macro_rules! make_method {
        ($name:ident) => {
            fn $name(&self) {}
        };
    }

    macro_rules! make_trait {
        () => {
            pub trait MyTrait {
                make_method! { by_macro }
                fn not_by_macro(&self) {}
            }
        }
    }

    make_trait!();

    pub struct Foo {}

    impl MyTrait for Foo {}
}

fn main() {
    use other_mod::{Foo, MyTrait};
    let f = Foo {};
    f.$0
}
"#,
        expect![[r#"
            me by_macro() (as MyTrait)     fn(&self)
            me not_by_macro() (as MyTrait) fn(&self)
        "#]],
    )
}

#[test]
fn completes_const_in_pub_trait_generated_by_macro() {
    check_no_kw(
        r#"
mod other_mod {
    macro_rules! make_const {
        ($name:ident) => {
            const $name: u8 = 1;
        };
    }

    pub trait MyTrait {
        make_const! { by_macro }
    }

    pub struct Foo {}

    impl MyTrait for Foo {}
}

fn main() {
    use other_mod::{Foo, MyTrait};
    let f = Foo {};
    Foo::$0
}
"#,
        expect![[r#"
            ct by_macro (as MyTrait) pub const by_macro: u8
        "#]],
    )
}

#[test]
fn completes_locals_from_macros() {
    check_no_kw(
        r#"

macro_rules! x {
    ($x:ident, $expr:expr) => {
        let $x = 0;
        $expr
    };
}
fn main() {
    x! {
        foobar, {
            f$0
        }
    };
}
"#,
        expect![[r#"
            fn main()          fn()
            lc foobar           i32
            ma x!(…) macro_rules! x
            bt u32              u32
        "#]],
    )
}

#[test]
fn regression_12644() {
    check_no_kw(
        r#"
macro_rules! __rust_force_expr {
    ($e:expr) => {
        $e
    };
}
macro_rules! vec {
    ($elem:expr) => {
        __rust_force_expr!($elem)
    };
}

struct Struct;
impl Struct {
    fn foo(self) {}
}

fn f() {
    vec![Struct].$0;
}
"#,
        expect![[r#"
            me foo() fn(self)
        "#]],
    );
}

#[test]
fn completes_after_colon_with_trigger() {
    check_with_trigger_character(
        r#"
//- minicore: option
fn foo { ::$0 }
"#,
        Some(':'),
        expect![[r#"
            md core
        "#]],
    );
    check_with_trigger_character(
        r#"
//- minicore: option
fn foo { /* test */::$0 }
"#,
        Some(':'),
        expect![[r#"
            md core
        "#]],
    );

    check_with_trigger_character(
        r#"
fn foo { crate::$0 }
"#,
        Some(':'),
        expect![[r#"
            fn foo() fn()
        "#]],
    );

    check_with_trigger_character(
        r#"
fn foo { crate:$0 }
"#,
        Some(':'),
        expect![""],
    );
}

#[test]
fn completes_after_colon_without_trigger() {
    check_with_trigger_character(
        r#"
fn foo { crate::$0 }
"#,
        None,
        expect![[r#"
            fn foo() fn()
        "#]],
    );

    check_with_trigger_character(
        r#"
fn foo { crate:$0 }
"#,
        None,
        expect![""],
    );
}

#[test]
fn no_completions_in_invalid_path() {
    check(
        r#"
fn foo { crate:::$0 }
"#,
        expect![""],
    );
    check_no_kw(
        r#"
fn foo { crate::::$0 }
"#,
        expect![""],
    )
}

#[test]
fn completes_struct_via_doc_alias_in_fn_body() {
    check(
        r#"
#[doc(alias = "Bar")]
struct Foo;

fn here_we_go() {
    $0
}
"#,
        expect![[r#"
            fn here_we_go()   fn()
            st Foo (alias Bar) Foo
            bt u32             u32
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw impl for
            kw let
            kw letm
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
}

#[test]
fn completes_struct_via_multiple_doc_aliases_in_fn_body() {
    check(
        r#"
#[doc(alias("Bar", "Qux"))]
#[doc(alias = "Baz")]
struct Foo;

fn here_we_go() {
    B$0
}
"#,
        expect![[r#"
            fn here_we_go()             fn()
            st Foo (alias Bar, Qux, Baz) Foo
            bt u32                       u32
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw impl for
            kw let
            kw letm
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
}

#[test]
fn completes_field_name_via_doc_alias_in_fn_body() {
    check(
        r#"
struct Foo {
    #[doc(alias = "qux")]
    bar: u8
};

fn here_we_go() {
    let foo = Foo { q$0 }
}
"#,
        expect![[r#"
            fd bar (alias qux) u8
        "#]],
    );
}

#[test]
fn completes_struct_fn_name_via_doc_alias_in_fn_body() {
    check(
        r#"
struct Foo;
impl Foo {
    #[doc(alias = "qux")]
    fn bar() -> u8 { 1 }
}

fn here_we_go() {
    Foo::q$0
}
"#,
        expect![[r#"
            fn bar() (alias qux) fn() -> u8
        "#]],
    );
}

#[test]
fn completes_method_name_via_doc_alias_in_fn_body() {
    check(
        r#"
struct Foo {
    bar: u8
}
impl Foo {
    #[doc(alias = "qux")]
    fn baz(&self) -> u8 {
        self.bar
    }
}

fn here_we_go() {
    let foo = Foo { field: 42 };
    foo.q$0
}
"#,
        expect![[r#"
            fd bar                            u8
            me baz() (alias qux) fn(&self) -> u8
            sn box                Box::new(expr)
            sn call               function(expr)
            sn const                    const {}
            sn dbg                    dbg!(expr)
            sn dbgr                  dbg!(&expr)
            sn deref                       *expr
            sn let                           let
            sn letm                      let mut
            sn match               match expr {}
            sn ref                         &expr
            sn refm                    &mut expr
            sn return                return expr
            sn unsafe                  unsafe {}
        "#]],
    );
}

#[test]
fn completes_fn_name_via_doc_alias_in_fn_body() {
    check(
        r#"
#[doc(alias = "qux")]
fn foo() {}
fn bar() { qu$0 }
"#,
        expect![[r#"
            fn bar()             fn()
            fn foo() (alias qux) fn()
            bt u32                u32
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw impl for
            kw let
            kw letm
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
}

#[test]
fn completes_struct_name_via_doc_alias_in_another_mod() {
    check(
        r#"
mod foo {
    #[doc(alias = "Qux")]
    pub struct Bar(u8);
}

fn here_we_go() {
    use foo;
    let foo = foo::Q$0
}
"#,
        expect![[r#"
            st Bar (alias Qux) Bar
        "#]],
    );
}

#[test]
fn completes_use_via_doc_alias_in_another_mod() {
    check(
        r#"
mod foo {
    #[doc(alias = "Qux")]
    pub struct Bar(u8);
}

fn here_we_go() {
    use foo::Q$0;
}
"#,
        expect![[r#"
            st Bar (alias Qux) Bar
        "#]],
    );
}

#[test]
fn completes_flyimport_with_doc_alias_in_another_mod() {
    check(
        r#"
mod foo {
    #[doc(alias = "Qux")]
    pub struct Bar();
}

fn here_we_go() {
    let foo = Bar$0
}
"#,
        expect![[r#"
            fn here_we_go()                  fn()
            md foo
            st Bar (alias Qux) (use foo::Bar) Bar
            bt u32                            u32
            kw const
            kw crate::
            kw false
            kw for
            kw if
            kw if let
            kw loop
            kw match
            kw return
            kw self::
            kw true
            kw unsafe
            kw while
            kw while let
        "#]],
    );
}

#[test]
fn completes_only_public() {
    check(
        r#"
//- /e.rs
pub(self) fn i_should_be_hidden() {}
pub(in crate::e) fn i_should_also_be_hidden() {}
pub fn i_am_public () {}

//- /lib.rs crate:krate
pub mod e;

//- /main.rs deps:krate crate:main
use krate::e;
fn main() {
    e::$0
}"#,
        expect![[r#"
            fn i_am_public() fn()
        "#]],
    )
}

#[test]
fn completion_filtering_excludes_non_identifier_doc_aliases() {
    check_edit(
        "PartialOrdcmporder",
        r#"
#[doc(alias = ">")]
#[doc(alias = "cmp")]
#[doc(alias = "order")]
trait PartialOrd {}

struct Foo<T: Partial$0
"#,
        r#"
#[doc(alias = ">")]
#[doc(alias = "cmp")]
#[doc(alias = "order")]
trait PartialOrd {}

struct Foo<T: PartialOrd
"#,
    );
}

fn check_signatures(src: &str, kind: CompletionItemKind, reduced: Expect, full: Expect) {
    const FULL_SIGNATURES_CONFIG: crate::CompletionConfig<'_> = {
        let mut x = TEST_CONFIG;
        x.full_function_signatures = true;
        x
    };

    // reduced signature
    let completion = do_completion_with_config(TEST_CONFIG, src, kind);
    assert!(completion[0].detail.is_some());
    reduced.assert_eq(completion[0].detail.as_ref().unwrap());

    // full signature
    let completion = do_completion_with_config(FULL_SIGNATURES_CONFIG, src, kind);
    assert!(completion[0].detail.is_some());
    full.assert_eq(completion[0].detail.as_ref().unwrap());
}

#[test]
fn respects_full_function_signatures() {
    check_signatures(
        r#"
pub fn foo<'x, T>(x: &'x mut T) -> u8 where T: Clone, { 0u8 }
fn main() { fo$0 }
"#,
        CompletionItemKind::SymbolKind(ide_db::SymbolKind::Function),
        expect!("fn(&'x mut T) -> u8"),
        expect!("pub fn foo<'x, T>(x: &'x mut T) -> u8 where T: Clone,"),
    );

    check_signatures(
        r#"
struct Foo;
struct Bar;
impl Bar {
    pub const fn baz(x: Foo) -> ! { loop {} };
}

fn main() { Bar::b$0 }
"#,
        CompletionItemKind::SymbolKind(ide_db::SymbolKind::Function),
        expect!("const fn(Foo) -> !"),
        expect!("pub const fn baz(x: Foo) -> !"),
    );

    check_signatures(
        r#"
struct Foo;
struct Bar;
impl Bar {
    pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> ! { loop {} };
}

fn main() {
    let mut bar = Bar;
    bar.b$0
}
"#,
        CompletionItemKind::SymbolKind(SymbolKind::Method),
        expect!("const fn(&'foo mut self, &'foo Foo) -> !"),
        expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"),
    );
}

#[test]
fn skips_underscore() {
    check_with_trigger_character(
        r#"
fn foo(_$0) { }
"#,
        Some('_'),
        expect![[r#""#]],
    );
    check_with_trigger_character(
        r#"
fn foo(_: _$0) { }
"#,
        Some('_'),
        expect![[r#""#]],
    );
    check_with_trigger_character(
        r#"
fn foo<T>() {
    foo::<_$0>();
}
"#,
        Some('_'),
        expect![[r#""#]],
    );
    // underscore expressions are fine, they are invalid so the user definitely meant to type an
    // underscored name here
    check_with_trigger_character(
        r#"
fn foo() {
    _$0
}
"#,
        Some('_'),
        expect![[r#"
            fn foo()  fn()
            bt u32     u32
            kw async
            kw const
            kw crate::
            kw enum
            kw extern
            kw false
            kw fn
            kw for
            kw if
            kw if let
            kw impl
            kw impl for
            kw let
            kw letm
            kw loop
            kw match
            kw mod
            kw return
            kw self::
            kw static
            kw struct
            kw trait
            kw true
            kw type
            kw union
            kw unsafe
            kw use
            kw while
            kw while let
            sn macro_rules
            sn pd
            sn ppd
        "#]],
    );
}

#[test]
fn no_skip_underscore_ident() {
    check_with_trigger_character(
        r#"
fn foo(a_$0) { }
"#,
        Some('_'),
        expect![[r#"
            kw mut
            kw ref
        "#]],
    );
    check_with_trigger_character(
        r#"
fn foo(_: a_$0) { }
"#,
        Some('_'),
        expect![[r#"
            bt u32 u32
            kw crate::
            kw dyn
            kw fn
            kw for
            kw impl
            kw self::
        "#]],
    );
    check_with_trigger_character(
        r#"
fn foo<T>() {
    foo::<a_$0>();
}
"#,
        Some('_'),
        expect![[r#"
            tp T
            bt u32 u32
            kw crate::
            kw dyn
            kw fn
            kw for
            kw impl
            kw self::
        "#]],
    );
}

#[test]
fn fn_generic_params_const_param_snippet() {
    check_edit("const", "fn foo<c$0>() {}", "fn foo<const $1: $0>() {}");
    check_edit("const", "fn foo<T, c$0>() {}", "fn foo<T, const $1: $0>() {}");
    check(
        r#"
fn foo<T: $0>() {}
"#,
        expect![[r#"
            kw crate::
            kw self::
        "#]],
    );
    check(
        r#"
fn foo<const N: $0>() {}
"#,
        expect![[r#"
            bt u32 u32
            kw crate::
            kw dyn
            kw fn
            kw for
            kw impl
            kw self::
        "#]],
    );
}
